/* EventUtils.js
 *
 * Copyright 2006, Tim Dwelle.
 *
 * Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.  See
 * the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

$package("dowry.util");

/**
 * Helper class to manage DOM event listeners.  This
 * implementation uses its own homegrwon event manager...
 * but it can be easily replaced if you would rather use the
 * event management utilities in your favorite Javascript
 * library.
 *
 */
$class("EventUtils").$as(
{
    /**
     * Simple map of events that are handled as keyboard
     * events.
     *
     */
    KEYBOARD_EVENTS : { keyup : true,
                        keydown : true,
                        keypress : true },

    /**
     * Simple map of events that are handled as mouse
     * events.
     *
     */
    MOUSE_EVENTS : { click     : true,
                     mousedown : true,
                     mouseup   : true,
                     mouseover : true,
                     mousemove : true,
                     moseout   : true },

    /**
     * Simple map of events that are handled as UI events.
     *
     */
    UI_EVENTS : { blur  : true,
                  focus : true,
                  resize : true,
                  scroll : true },


    /**
     * Adds an event listener to the specified object.
     *
     *
     * @param obj        the object to add a listener to
     *
     * @param eventName  the string name of the event to add
     *                   (excluding the 'on' prefix)
     *
     * @param func       the function to listen to the event
     *
     * @return           true, if the event listener was
     *                   successfully added; false otherwise
     *
     */
    addEvent : function(obj, eventName, func)
    {
        var rc = false;

        obj = $(obj);
        if (obj && eventName && func)
        {
            if (obj.addEventListener)
            {
                obj.addEventListener(eventName, func, false);
                rc = true;
            }
            else if (obj.attachEvent)
            {
                rc = obj.attachEvent("on"+eventName, func);
            }
            else
            {
                // no event listener
                if (!obj["on"+eventName])
                {
                    obj["on"+eventName] = this._eventListener();
                }

                // an event listener, but not ours...
                else if (!obj["on"+eventName].$events)
                {
                    var orig = obj["on"+eventName];
                    obj["on"+eventName] = this._eventListener();
                    obj["on"+eventName].$events[0] = orig;
                }

                // add the new listener
                var events = obj["on"+eventName].$events;
                events[events.length] = func;
                rc = true;
            }
        }

        return rc;
    },

    /**
     * Programmatically dispatch an event on the specified
     * object.
     *
     * @param obj        the object to call the event on
     *
     * @param eventName  the string name of the event to add
     *                   (excluding the 'on' prefix)
     *
     * @param args       (optional) a map of named arguments
     *                   to dipatch as appropriate for the
     *                   event, such as clientX, clientY,
     *                   etc.
     *
     */
    dispatchEvent : function(obj, eventName, args)
    {
        obj = $(obj);

        if (obj && eventName)
        {
            if (document.createEvent && obj.dispatchEvent)
            {
                var eventType = "HTMLEvents";

                if (this.UI_EVENTS[eventName])
                {
                    eventType = "UIEvents";
                }
                else if (this.KEYBOARD_EVENTS[eventName])
                {
                    eventType = "KeyboardEvent";
                }
                else if (this.MOUSE_EVENTS[eventName])
                {
                    eventType = "MouseEvents";
                }

                if (!args)
                {
                    args = {};
                }

                var canBubble = args.canBubble || true;
                var cancelable = args.cancelable || true;
                var view = args.view || window;
                var detail = args.detail || 1;
                var screenX = args.screenX || 0;
                var screenY = args.screenY || 0;
                var clientX = args.clientX || 0;
                var clientY = args.clientY || 0;
                var ctrlKey = args.ctrlKey || false;
                var altKey = args.altKey || false;
                var shiftKey = args.shiftKey || false;
                var metaKey = args.metaKey || false;
                var button = args.button || 0;
                var relatedTarget = args.relatedTarget || null;

                var evt = document.createEvent(eventType);

                if (eventType == "UIEvents")
                {
                    evt.initUIEvent(eventName,
                                    canBubble,
                                    cancelable,
                                    view,
                                    detail);
                }
                else if (eventType == "MouseEvents")
                {
                    evt.initMouseEvent(eventName,
                                       canBubble,
                                       cancelable,
                                       view,
                                       detail,
                                       screenX,
                                       screenY,
                                       clientX,
                                       clientY,
                                       ctrlKey,
                                       altKey,
                                       shiftKey,
                                       metaKey,
                                       button,
                                       relatedTarget);
                }
                else
                {
                    evt.initEvent(eventName, canBubble, cancelable);
                }

                obj.dispatchEvent(evt);
            }
            else if (document.createEventObject && obj.fireEvent)
            {
                var evt = document.createEventObject();

                if (args)
                {
                    for (var key in args)
                    {
                        evt[key] = args[key];
                    }
                }

                obj.fireEvent("on"+eventName, evt);
            }
        }
    },

    /**
     * Removes an event listener from the specified object.
     *
     *
     * @param obj        the object to remove a listener
     *                   from
     *
     * @param eventName  the string name of the event to
     *                   remove (excluding the 'on' prefix)
     *
     * @param func       the function that listened to the
     *                   event
     *
     */
    removeEvent : function(obj, eventName, func)
    {
        obj = $(obj);
        if (obj && eventName && func)
        {
            if (obj.removeEventListener)
            {
                obj.removeEventListener(eventName, func, false);
            }
            else if (obj.detachEvent)
            {
                obj.detachEvent("on"+eventName, func);
            }
            else if (obj["on"+eventName] &&
                     obj["on"+eventName].$events)
            {
                var events = obj["on"+eventName].$events;
                for (var i=0; i < events.length; i++)
                {
                    if (events[i] == func)
                    {
                        events[i] = null;
                    }
                }
            }
        }
    },


    /**
     * To support environemnts where neither
     * addEventListener nor attachEvent are provided, this
     * returns an event listener function that calls all the
     * attached event functions (stored in the function's
     * <code>$events</code> array).
     *
     * @return  an event listener function
     *
     */
    _eventListener : function()
    {
        var f = function()
        {
            var events = arguments.callee.$events;
            for (var i=0; i < events.length; i++)
            {
                if (events[i])
                {
                    events[i].apply(null, arguments);
                }
            }
        };

        f.$events = new Array();
        return f;
    }
});